package com.clp.protocol.iec104.iec104.client.state;

import com.clp.protocol.core.exception.FailedToSendFrameException;
import com.clp.protocol.iec104.iec104.apdu.Apdu;
import com.clp.protocol.iec104.iec104.client.InMaster;
import com.clp.protocol.iec104.iec104.client.async.MasterPromise;
import com.clp.protocol.iec104.iec104.common.res.SendTestFrVRes;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.Promise;
import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.Vector;
import java.util.concurrent.TimeUnit;

/**
 * T0T1T2T3 过程状态信息
 */
@Slf4j
public class T0T1T2T3MasterState extends AbstractMasterState {

    private enum State {
        TEST_FR_V,
        TEST_FR_C
    }

    private final int T0;
    private final int T1;
    private final int T2;
    private final int T3;

    private boolean ensureSendIType;       // 确认发送了I帧
    private Date lastSendITypeTime;        // 上次发送I帧时间
    private boolean ensureSendSType;       // 确认发送了S帧
    private Date lastSendSTypeTime;        // 上次发送S帧时间
    private boolean ensureSendUType;       // 确认发送了U帧
    private Date lastSendUTypeTime;        // 上次发送U帧时间

    private Date lastRecvTime;             // 上次接收帧的时间
    private boolean ensureRecvIType;       // 确认接收了I帧
    private Date lastRecvITypeTime;        // 上次接收I帧时间
    private boolean ensureRecvSType;       // 确认接收了S帧
    private Date lastRecvSTypeTime;        // 上次接收S帧时间
    private boolean ensureRecvUType;       // 确认接收了U帧
    private Date lastRecvUTypeTime;        // 上次接收U帧时间

    private volatile State state;
    private final Vector<MasterPromise<SendTestFrVRes>> promises;


    protected T0T1T2T3MasterState(InMasterStateManager stateManager, int T0, int T1, int T2, int T3) {
        super(stateManager);

        this.T0 = T0;
        this.T1 = T1;
        this.T2 = T2;
        this.T3 = T3;
        this.promises = new Vector<>();

        resetState();
    }

    @Override
    protected Future<Void> resetState() {
        Promise<Void> promise = createPromise(Void.class);
        this.ensureSendIType = true;
        this.lastSendITypeTime = new Date();
        this.ensureSendSType = true;
        this.lastSendSTypeTime = new Date();
        this.ensureSendUType = true;
        this.lastSendUTypeTime = new Date();
        this.lastRecvTime = new Date();
        this.ensureRecvIType = true;
        this.lastRecvITypeTime = new Date();
        this.ensureRecvSType = true;
        this.lastRecvSTypeTime = new Date();
        this.ensureRecvUType = true;
        this.lastRecvUTypeTime = new Date();
        this.state = State.TEST_FR_C;
        this.promises.clear();
        return promise.setSuccess(null);
    }

    @Override
    protected Future<Void> refreshState() {
        return resetState();
    }

    @Override
    public Apdu updateStateBySending(Apdu apdu) {
        if (apdu == null) return null;

        // 检查apdu合法性
        if (apdu.isUType()) {
            switch (apdu.castToUType().getUApci().getCtrlArea().getUCtrlType()) {
                case U_TEST_FR_V:
                    if (state == State.TEST_FR_C) {
                        // 发送成功
                        promises.forEach(promise -> {
                            promise.getRes().setSendSuccess(true);
                        });
                        state = State.TEST_FR_V;
                    } else {
                        // 发送失败
                        promises.forEach(promise -> {
                            promise.getRes().setSendSuccess(false).setRecvAckSuccess(false);
                            promise.setFailure(new FailedToSendFrameException(apdu));
                        });
                        promises.clear();
                        log.warn("当前正在进行链路测试，不能重复发送链路测试帧！");
                        return null;
                    }
                    break;
            }
        }

        // 更新链路状态
        if (apdu.isUType()) {
            // 确认发送了U帧
            ensureSendUType = true;
            // 更新上次发送U帧的时间
            lastSendUTypeTime = new Date();
            // 设置为未接收到发出的响应（发送激活U帧需要接收到确认U帧）
            switch (apdu.castToUType().getUApci().getCtrlArea().getUCtrlType()) {
                case U_TEST_FR_V:
                case U_STOP_DT_V:
                case U_START_DT_V:
                    ensureRecvUType = false;
                    break;
                default:
                    ensureRecvUType = true;
            }
        } else if (apdu.isSType()) {
            // 确认发送了S帧
            ensureSendSType = true;
            // 发送S帧不需要等待对方的认可
        } else if (apdu.isIType()) {
            // 确认发送了I帧
            ensureSendIType = true;
            // 更新上次发送U帧的时间
            lastSendITypeTime = new Date();
            // 设置为未接收到发出的响应（发送I帧要接收到I帧或S帧的认可）
            ensureRecvSType = false;
            ensureRecvIType = false;
        }
        return apdu;
    }

    @Override
    public Apdu updateStateByRecving(Apdu apdu) {
        if (apdu == null) return null;

        // 检查apdu合法性
        if (apdu.isUType()) {
            switch (apdu.castToUType().getUApci().getCtrlArea().getUCtrlType()) {
                case U_TEST_FR_C:
                    if (state == State.TEST_FR_V) {
                        promises.forEach(promise -> {
                            promise.getRes().setRecvAckSuccess(true);
                            promise.setSuccess();
                        });
                        promises.clear();
                        state = State.TEST_FR_C;
                    } else {
                        log.warn("接收到无效的链路测试确认报文！");
                    }
                    break;
            }
        }

        // 更新链路状态
        lastRecvTime = new Date();
        if (apdu.isUType()) { // 如果是U帧
            // 确认接收了U帧
            ensureRecvUType = true;
            // 更新上次接收U帧时间
            lastRecvUTypeTime = new Date();
            // 设置还未发送对该帧的确认（接收到激活U帧要发送确认U帧）
            switch (apdu.castToUType().getUApci().getCtrlArea().getUCtrlType()) {
                case U_START_DT_V:
                case U_STOP_DT_V:
                case U_TEST_FR_V:
                    ensureSendUType = false;
                    // 发送链路测试确认
                    getStateManager().getInMaster().getApduSender().sendUApduOfTestFrC();
                    break;
                default:
                    ensureSendUType = true;
            }
        } else if (apdu.isSType()) { // 如果是S帧
            // 确认接收了S帧
            ensureRecvSType = true;
            // 更新上次接收S帧时间
            lastRecvSTypeTime = new Date();
            // 接收到S帧不需要对该帧发送认可消息
        } else if (apdu.isIType()) { // 如果是I帧
            // 确认接收了I帧
            ensureRecvIType = true;
            // 更新上次接收I帧时间
            lastRecvITypeTime = new Date();
            // 设置为还未发送对该I帧的确认（接收到I帧要发送I帧或S帧的认可）
            ensureSendIType = false;
            ensureSendSType = false;
        }
        return apdu;
    }

    @Override
    protected ScheduledTask getScheduledTask() {
        return new ScheduledTask(0, 1, TimeUnit.SECONDS) {
            @Override
            public void run() {
                Date nowTime = new Date();  // 当前时间
                InMaster master = getStateManager().getInMaster();
                // T1：如果发送了U帧后，T1时间内没有接收到U帧认可，那么认为连接出现问题，需要重新建立连接
                if (ensureSendUType && !ensureRecvUType &&
                        nowTime.getTime() - lastSendUTypeTime.getTime() > T1 * 1000L) {
                    // 如果当前状态为已经发送了链路测试帧
                    if (state == State.TEST_FR_V) {
                        promises.forEach(promise -> {
                            promise.getRes().setRecvAckSuccess(false);
                            promise.setFailure(new RuntimeException("链路测试超时"));
                        });
                        promises.clear();
                        state = State.TEST_FR_C;
                    }
                    log.warn("Control: 发送U帧后，T1时间内未接收到从站 {}:{} 的认可消息，即将自动重连",
                            master.remoteHost(), master.remotePort());
                    getStateManager().getInMaster().getConnector().tryAutoReconnect();
                }
                // T1：如果发送了I帧后，T1时间内没有接收到I帧或S帧认可，那么认为连接出现问题，需要重新建立连接
                if (ensureSendIType &&
                        !(ensureRecvIType || ensureRecvSType) &&
                        nowTime.getTime() - lastSendITypeTime.getTime() > T1 * 1000L) {
                    log.warn("Control: 发送I帧后，T1时间内未接收到从站 {}:{} 的认可消息，即将自动重连",
                            master.remoteHost(), master.remotePort());
                    getStateManager().getInMaster().getConnector().tryAutoReconnect();
                }

                // T2：如果收到I格式报文后，没有发送S帧或I帧，且T2时间内没有再接收到新的I帧，则要发送S格式帧进行认可
                if (ensureRecvIType &&
                        (!(ensureSendSType || ensureSendIType)) &&
                        nowTime.getTime() - lastRecvITypeTime.getTime() > T2 * 1000L) {
                    log.info("Control: T2时间后没有继续收到I格式帧，本站也没有发送S帧或I帧，发出S帧进行认可\n");
                    master.getApduSender().sendSApduOfAck();
                }

                // T3：如果接收到任意格式的帧后，T3时间内没有再接收到新的任意格式帧，则发送链路测试帧
                if ((ensureRecvUType || ensureRecvSType || ensureRecvIType)
                        && nowTime.getTime() - lastRecvTime.getTime() > T3 * 1000L) {
                    log.info("Control: T3时间内没有继续收到任何格式的帧，发送测试U帧\n");
                    master.getApduSender().sendUApduOfTestFrV();
                }
            }
        };
    }

    @Override
    @SuppressWarnings("unchecked")
    public <V> MasterPromise<V> register(MasterPromise<V> sendPromise) {
        if (sendPromise == null || sendPromise.getRes() == null) return sendPromise;
        if (sendPromise.getRes() instanceof SendTestFrVRes) {
            promises.add(((MasterPromise<SendTestFrVRes>) sendPromise));
            return null;
        }
        return sendPromise;
    }

    public boolean isTestingFr() {
        return state == State.TEST_FR_V;
    }
}
